package cn.wjh.business.service.impl;

import cn.wjh.business.service.IMailService;
import cn.wjh.common.core.domain.entity.SysUser;
import cn.wjh.common.core.redis.RedisCache;
import cn.wjh.common.exception.ServiceException;
import cn.wjh.common.utils.StringUtils;
import cn.wjh.business.domain.vo.MailVo;
import cn.wjh.business.domain.vo.RegisterVo;
import cn.wjh.system.service.ISysUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfig;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/*邮箱服务*/
@Service
@Slf4j
public class MailServiceImpl implements IMailService {
	@Resource
	private RedisCache redisCache;

	@Resource
	private JavaMailSenderImpl javaMailSender;
	@Resource
	private FreeMarkerConfig freeMarkerConfig;

	@Value("${spring.mail.username}")
	private String sendMailer;

	@Resource
	private ISysUserService userService;

	/**
	 * 校验邮箱
	 *
	 * @param to      接收人
	 * @param subject 邮箱主题
	 * @param content 邮箱内容
	 */
	private void validateMail(String to, String subject, String content) {
		if (StringUtils.isEmpty(to)) {
			throw new ServiceException("邮件收信人不能为空");
		}
		if (StringUtils.isEmpty(subject)) {
			throw new ServiceException("邮件主题不能为空");
		}
		if (StringUtils.isEmpty(content)) {
			throw new ServiceException("邮件内容不能为空");
		}
	}

	/**
	 * 发送邮件
	 *
	 * @param to      接收人
	 * @param subject 邮件主题
	 * @param content 邮件内容
	 * @param isHtml  是否为html文本
	 */
	private void sendMailMessage(String to, String subject, String content, boolean isHtml) {

		try {
			//true 代表支持复杂的类型
			MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(), true);
			//邮件发信人
			mimeMessageHelper.setFrom(sendMailer);
			//邮件收信人  1或多个
			mimeMessageHelper.setTo(to.split(","));
			//邮件主题
			mimeMessageHelper.setSubject(subject);
			//邮件内容
			mimeMessageHelper.setText(content, isHtml);
			//邮件发送时间
			mimeMessageHelper.setSentDate(new Date());

			//发送邮件
			javaMailSender.send(mimeMessageHelper.getMimeMessage());
			log.info("发送邮件成功：{} -> {}", sendMailer, to);

		} catch (MessagingException e) {
			e.printStackTrace();
			log.error("发送邮件失败：" + e.getMessage());
		}
	}

	/**
	 * 发送邮箱验证码
	 *
	 * @param to    接收人
	 * @param title 标题
	 */
	public boolean sendVerificationCode(String to, String title) {
		if (title.equals("平台注册")) {
			/*校验邮箱是否唯一*/
			LambdaQueryWrapper<SysUser> queryWrapper = new QueryWrapper<SysUser>().lambda()
					.eq(SysUser::getEmail, to);
			long count = userService.count(queryWrapper);
			if (count > 0) throw new ServiceException("发送失败!该邮箱已被绑定!",400);
		}
		String limit_key = title.concat(":" + to).concat(":limit");//邮箱频繁发送限制key title:to:limit
		isLimit(limit_key);//校验是否频繁发送

		/*生成验证码*/
		int rand = new Random().nextInt(1000_000);
		String code = String.format("%06d", rand);
		String code_key = title.concat(":" + to).concat(":code");//邮箱验证码存储key title:to:code

		Map<String, String> model = new HashMap<>();
		model.put("title", title);
		model.put("code", code);

		String content;//邮件内容

		try {
			content = FreeMarkerTemplateUtils.processTemplateIntoString(freeMarkerConfig.getConfiguration().getTemplate("verificationCode.ftl"), model);
		} catch (IOException | TemplateException e) {
			log.error("加载邮件模板失败", e);
			throw new ServiceException("加载邮件模板失败");
		}
		/*校验*/
		validateMail(to, title, content);
		/*发送*/
		sendMailMessage(to, title, content, true);

		log.info("验证码已发送到:{},验证码为:{}", to, code);
		/*存储验证码*/
		redisCache.setCacheObject(code_key, code, 5, TimeUnit.MINUTES);
		redisCache.setCacheObject(limit_key, true, 1, TimeUnit.MINUTES);

		return true;

	}

	/**
	 * 校验是否频繁发送验证码
	 *
	 * @param limitKey 键
	 */
	private void isLimit(String limitKey) {
		if (redisCache.hasKey(limitKey)) {
			throw new ServiceException("验证码发送过于频繁,请稍等一下再发送喔!", 400);
		}
	}


	/**
	 * 验证邮箱验证码是否正确
	 *
	 * @param title 邮件主题
	 * @param email 邮箱
	 * @param code  验证码
	 */
	public boolean verifyCode(String title, String email, String code) {
		String code_key = title.concat(":" + email).concat(":code");//邮箱验证码存储key title:to:code
		String savedCode = redisCache.getCacheObject(code_key);
		return code.equals(savedCode);
	}

	@Override
	public boolean verifyCode(MailVo mailVo) {
		String code_key = mailVo.getTitle().concat(":" + mailVo.getEmail()).concat(":code");//邮箱验证码存储key title:to:code
		String savedCode = redisCache.getCacheObject(code_key);
		return mailVo.getCode().equals(savedCode);
	}

	@Override
	public boolean verifyCode(RegisterVo mailVo) {
		String code_key = mailVo.getTitle().concat(":" + mailVo.getEmail()).concat(":code");//邮箱验证码存储key title:to:code
		String savedCode = redisCache.getCacheObject(code_key);
		return mailVo.getCode().equals(savedCode);
	}
}
